﻿/*
 * This file is part of CommunitySettlers.
 *
 * CommunitySettlers is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.

 * CommunitySettlers is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.

 * You should have received a copy of the GNU General Public License
 * along with CommunitySettlers.  If not, see <http://www.gnu.org/licenses/>.
 * 
 * Author: Christoph Husse
 * 
 * Also checkout our homepage: http://communitysettlers.codeplex.com/
 */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;

namespace SettlersEngine
{
    internal partial class RuntimeGrid
    {


        public void Register(IPositionProvider inObject)
        {
            if (inObject.GridNode != null)
                throw new InvalidOperationException("Object has already been added to grid.");

            if (!CanPlaceObject(inObject))
                throw new InvalidOperationException("Object can not be placed at this position.");

            if (inObject is IMovableObject)
            {
                IMovableObject movable = inObject as IMovableObject;

                movable.UserContext = Map.Factory.CreateMoveable(Map, movable);
                movable.GridNode = m_Movables.AddLast(movable);

                m_MovableManager.Register(movable);
            }
            else if (inObject is IBuildableObject)
            {
                IBuildableObject buildable = inObject as IBuildableObject;

                if (buildable.Config == null)
                    throw new ArgumentNullException("Building has no config.");

                buildable.UserContext = Map.Factory.CreateBuilding(Map, buildable);
                buildable.GridNode = m_Buildings.AddLast(buildable);

                foreach (var solid in buildable.Config.SolidBoundaries)
                {
                    Point pos = buildable.GridPosition;

                    for (int x = solid.X; x < solid.X + solid.Width; x++)
                    {
                        for (int y = solid.Y; y < solid.Y + solid.Height; y++)
                        {
                            RuntimeGridCell item = m_Cells[x + pos.X, y + pos.Y];

                            if (item.FixedObject != null)
                            {
                                if (item.FixedObject != buildable)
                                    throw new InvalidOperationException();
                            }
                            else
                                item.FixedObject = buildable;
                        }
                    }
                }

                Map.RaiseBuildSpaceChange();
            }
            else if (inObject is IResourceObject)
            {
                IResourceObject resource = inObject as IResourceObject;

                resource.UserContext = Map.Factory.CreateResource(Map, resource);
                inObject.GridNode = m_Resources.AddLast(resource);

                if (resource.Type == ResourceObjectType.Query)
                    resource.OnRequestedChanged += OnResourceRequestedChanged;

                m_Cells[resource.Position.XGrid, resource.Position.YGrid].Resource = resource;
            }
            else
                throw new ArgumentException("Unknown object type.");
        }

        public bool Unregister(IPositionProvider inObject)
        {
            if (inObject.GridNode == null)
                return false;

            if (inObject is IMovableObject)
            {
                IMovableObject movable = inObject as IMovableObject;

                m_Movables.Remove((LinkedListNode<IMovableObject>)inObject.GridNode);
                m_MovableManager.Unregister(movable);
            }
            else if (inObject is IBuildableObject)
            {
                IBuildableObject movable = inObject as IBuildableObject;

                m_Buildings.Remove((LinkedListNode<IBuildableObject>)inObject.GridNode);
            }
            else if (inObject is IResourceObject)
            {
                IResourceObject movable = inObject as IResourceObject;

                m_Resources.Remove((LinkedListNode<IResourceObject>)inObject.GridNode);
            }
            else
                throw new ArgumentException("Unknown object type.");

            inObject.GridNode = null;

            return true;
        }

        private void OnResourceRequestedChanged(IResourceObject inResource)
        {
            if (inResource.Type != ResourceObjectType.Query)
                throw new ApplicationException();

            // update priority queue entry
            if (inResource.QueryNode != null)
                m_ResQueryPrioQueue[inResource.QueryPriority].Remove(inResource.QueryNode);

            inResource.QueryNode = null;
            inResource.QueryPriority = m_ResQueryPrioQueue.Length - inResource.Requested;

            if (inResource.QueryPriority < m_ResQueryPrioQueue.Length)
                inResource.QueryNode = m_ResQueryPrioQueue[inResource.QueryPriority].AddLast(inResource);
        }
    }
}
